/*
* Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
* Description: decoded_mbuf_yuv_sender head file
*/
#ifndef DECODED_MBUF_YUV_SENDER_H
#define DECODED_MBUF_YUV_SENDER_H
#include "image_sender_base.h"
#include "mdc/cam/camera/cameradecodedmbufserviceinterface_skeleton.h"
#include "util.h"
#include "core/types.h"
#include "driver/ascend_hal.h"
#include "securec.h"

#include <cstdint>
#include <getopt.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/prctl.h>
#include <vector>
#include <unistd.h>

#include "acl.h"
#include "acl_rt.h"
#include "hi_dvpp.h"

#include "driver/ascend_hal.h"
#include "driver/ascend_hal_define.h"

using namespace std;
using namespace cv;
const int32_t MAX_NAME_LENGTH = 500;
using MbufSkeleton = mdc::cam::camera::skeleton::CameraDecodedMbufServiceInterfaceSkeleton;
class MbufVideoSender : public ImageSenderBase<MbufSkeleton>
{
    typedef void *(*pFUNC)(void *);
private:
    poolHandle handle;
    float lastTime = 0.0F;

    char g_input_file_name[MAX_NAME_LENGTH] = "test264.h264";
    char g_output_file_name[MAX_NAME_LENGTH] = "./outfile";
    uint32_t g_in_width = 3840;  // input stream width
    uint32_t g_in_height = 2160; // input stream height
    uint32_t g_in_format = 0;
    uint32_t g_in_bitwidth = 8;
    uint32_t g_out_width = 0;
    uint32_t g_out_height = 0;
    uint32_t g_out_format = 0;
    uint32_t g_out_width_stride = 4096;
    uint32_t g_out_height_stride = 4096;
    uint32_t g_is_write_file = 1;
    uint32_t g_chn_num = 1;
    uint32_t g_ref_frame_num = 8;
    uint32_t g_display_frame_num = 2;
    uint32_t g_output_order = 0;
    uint32_t g_send_times = 1;
    uint32_t g_send_interval = 0;
    uint32_t g_delay_time = 1;
    uint32_t g_alloc_num = 20;
    uint32_t g_start_chn_num = 0;
    uint32_t g_render = 0;
    uint32_t g_exit = 0;

    uint32_t g_chan_create_state[VDEC_MAX_CHN_NUM] = {0};
    uint64_t g_start_time = 0;

    std::vector<void *> g_out_buffer_pool[VDEC_MAX_CHN_NUM];
    pthread_mutex_t g_out_buffer_pool_lock[VDEC_MAX_CHN_NUM];

    uint32_t g_send_exit_state[VDEC_MAX_CHN_NUM] = {0};
    uint32_t g_get_exit_state[VDEC_MAX_CHN_NUM] = {0};

    pthread_t g_vdec_send_thread[VDEC_MAX_CHN_NUM] = {0};
    pthread_t g_vdec_get_thread[VDEC_MAX_CHN_NUM] = {0};
    uint32_t g_send_thread_id[VDEC_MAX_CHN_NUM] = {0};
    uint32_t g_get_thread_id[VDEC_MAX_CHN_NUM] = {0};

    uint8_t *g_frame_addr[VDEC_MAX_CHN_NUM][9999];
    uint64_t g_frame_len[VDEC_MAX_CHN_NUM][9999];
    uint64_t g_vdec_start_time[VDEC_MAX_CHN_NUM] = {0};
    uint64_t g_vdec_end_time[VDEC_MAX_CHN_NUM] = {0};
    uint64_t g_vdec_get_frame_cnt[VDEC_MAX_CHN_NUM] = {0};

    aclrtRunMode g_run_mode = ACL_HOST;
    std::string sourceYuvPath;
    FILE *fp_yuv;
    chrono::high_resolution_clock::time_point sec;
public:
    aclrtContext g_context = NULL;

public:
    MbufVideoSender() = default;
    MbufVideoSender(uint32_t id, cv::Size s, std::string dir) : ImageSenderBase<MbufSkeleton>(id, s, dir)
    {
        g_in_width = s.width;
        g_in_height = s.height;
        g_out_width = s.width;
        g_out_height = s.height;
        if (comPara.fileType == "h264") {
            g_in_format = 0;
        } else if (comPara.fileType == "h265") {
            g_in_format = 1;
        }
        g_out_format = 0;
        g_is_write_file = 1;
        g_out_width_stride = s.width;
        g_out_height_stride = s.height;
        sourceYuvPath = dir;
        if (dir.back() != '/') {
            sourceYuvPath += string("/") + "videoSource.yuv";
        } else {
            sourceYuvPath += "videoSource.yuv";
        }
#ifdef SAVE_SENDER_FILE
        fp_yuv = fopen(sourceYuvPath.c_str(), "wb+");
#endif
        sec = chrono::high_resolution_clock::now();
    }
    ~MbufVideoSender()
    {
        if (dataSkeleton != nullptr) {
            dataSkeleton->StopOfferService();
            dataSkeleton = nullptr;
        }
        if (handle != nullptr) {
            halBuffDeletePool(handle);
        }
    };

    bool RegisterSender()
    {
        std::cout << "Begin register MBUF image sender." << endl;
        auto result = ImageSenderBase<MbufSkeleton>::RegisterSender();
        std::cout << "Finished to register MBUF image sender." << endl;
        return result;
    }

    void VdecDecoded()
    {
        std::string path = dir;
        std::string postfix = comPara.fileType;
        bool isLoop = comPara.loop;
        cout << "dir : " << dir << "\t" << " postfix : " << postfix << endl;
        std::vector<std::string> files;
        int32_t fileCnt = getFilesBySort(path, files, comPara.fileType);
        if (fileCnt == 0) {
            cout << "PATH:" << path << " has no files!" << endl;
            return;
        }
        do {
            for (auto &file : files) {
                if (file.empty()) {
                    std::cerr << "File not exist, or corrupt.path: " << file << std::endl;
                    return;
                }
                memset(g_input_file_name, 0, MAX_NAME_LENGTH);
                strcpy(g_input_file_name, file.c_str());
                VdecInit();
                InitThreadPara();
                VdecProcess();
                VdecUninit();
            }
        } while (isLoop);
#ifdef SAVE_SENDER_FILE
        fclose(fp_yuv);
#endif
    }

    int32_t InitThreadPara()
    {
        g_send_exit_state[VDEC_MAX_CHN_NUM] = {0};
        g_get_exit_state[VDEC_MAX_CHN_NUM] = {0};
        g_vdec_send_thread[VDEC_MAX_CHN_NUM] = {0};
        g_vdec_get_thread[VDEC_MAX_CHN_NUM] = {0};
        g_send_thread_id[VDEC_MAX_CHN_NUM] = {0};
        g_get_thread_id[VDEC_MAX_CHN_NUM] = {0};
    }
    int32_t VdecInit()
    {
        print_parameter();
        int32_t ret = HI_SUCCESS;
        ret = hi_dvpp_init();
        if (ret != HI_SUCCESS) {
            printf("[%s][%d] hi_dvpp_init failed !\n", __FUNCTION__, __LINE__);
            return -1;
        }
        ret = vdec_create();
        if (ret != HI_SUCCESS) {
            printf("[%s][%d] VdecStart failed \n", __FUNCTION__, __LINE__);
            vdec_destroy();
            hi_dvpp_deinit();
            return -1;
        }
        init_outbuffer_lock();
        get_start_time();
        return ret;
    }
    void VdecUninit()
    {
        wait_vdec_end();
        show_decode_performance();
        destroy_outbuffer_lock();
        vdec_destroy();
        hi_dvpp_deinit();
    }
    void VdecProcess()
    {
        int32_t ret = HI_SUCCESS;
        ret = create_send_stream_thread();
        if (ret != 0) {
            stop_send_stream_thread();
        } else {
            ret = create_get_pic_thread();
            if (ret != 0) {
                stop_get_pic_thread();
            }
        }
    }
    int32_t BufCreatePool(const uint32_t blkSize, const uint32_t blkNum)
    {
        constexpr uint32_t alignSize = 32U;
        mpAttr attr;
        attr.devid = 0;
        attr.mGroupId = 0;
        attr.blkSize = ((blkSize + alignSize) - 1U) & (~(alignSize - 1));
        attr.blkNum = blkNum;
        attr.align = alignSize;
        attr.hugePageFlag = 1U;
        const int32_t ret = halBuffCreatePool(&attr, &handle);
        if (ret != DRV_ERROR_NONE) {
            std::cout << "halBuffCreatePool error: " << ret << std::endl;
            return -1;
        }
        return 0;
    }
    int32_t CopyToMbuf(uintptr_t &mbuf, const uint8_t *src, const uint32_t len) const
    {
        Mbuf *pMbuf = nullptr;
        int32_t ret = halMbufAllocByPool(handle, &pMbuf);
        if (ret != DRV_ERROR_NONE) {
            std::cout << "halMbufAllocByPool error: " << ret << std::endl;
            return -1;
        }
        if (pMbuf == nullptr) {
            std::cout << "halMbufAllocByPool mbuf null" << std::endl;
            return -1;
        }
        void *dst = nullptr;
        uint64_t bufSize = 0U;
        ret = halMbufGetDataPtr(pMbuf, &dst, &bufSize);
        if (ret != 0 || dst == nullptr) {
            std::cout << "MbufGetDataPtr error: " << ret << std::endl;
            halBuffFree(pMbuf);
            return -1;
        }
        if (len > bufSize) {
            std::cout << "Error: Src data length[" << len << "] > mbuf size[" << bufSize << "]" << std::endl;
            halBuffFree(pMbuf);
            return -1;
        }
        if (memcpy_s(reinterpret_cast<uint8_t *>(dst), static_cast<uint64_t>(bufSize), src, static_cast<uint64_t>(len)) != 0) {
            std::cout << "Error: Copy src data to mbuf error!" << std::endl;
            halBuffFree(pMbuf);
            return -1;
        }
        ret = halMbufSetDataLen(pMbuf, len);
        if (ret != DRV_ERROR_NONE) {
            std::cout << "halMbufSetDataLen error: " << ret << std::endl;
            halBuffFree(pMbuf);
            return -1;
        }
        mbuf = reinterpret_cast<uintptr_t>(pMbuf);
        return 0;
    }
    void FillImageAndSend(std::string imgPath, uint32_t seq, void *g_stopFlag) override {}
public:
    int32_t get_option(int32_t argc, char **argv);
    int32_t check_option();
    void print_parameter();
    void vdec_handle_signal(int32_t signo);
    int32_t vdec_create();
    void vdec_destroy();
    int32_t create_send_stream_thread();
    int32_t create_get_pic_thread();
    void stop_send_stream_thread();
    void stop_get_pic_thread();
    static void *send_stream(void *arg);
    static void *get_pic(void *arg);
    void get_every_frame(int32_t chanId, uint8_t *const inputFileBuf, uint32_t *const frameCount, uint32_t fileSize,
                         hi_payload_type type, uint8_t *devData);
    void delay_exec(uint64_t execTime, int32_t seconds);
    void wait_vdec_end();
    void show_decode_performance();
    void get_start_time();
    void init_outbuffer_lock();
    void destroy_outbuffer_lock();
    void release_outbuffer(uint32_t chanId);
    int32_t hi_dvpp_init();
    void hi_dvpp_deinit();
private:
    int32_t InitGrp(void);
    void get_current_time_us(uint64_t &timeUs);
    void save_yuv_file(FILE *const fd, hi_video_frame frame, uint32_t chanId);
    void FillYuvAndSend(uint8_t *ptr, int32_t w, int32_t h);
    void save_rgb_file(FILE *const fd, hi_video_frame frame, uint32_t chanId);
    void vdec_reset_chn(uint32_t chanId);
};

#endif //DECODED_MBUF_YUV_SENDER_H
